#include "chapter16.h"

template <typename T>
void Blob<T>::check(size_type i, const std::string &msg) const
{
    if (i >= data->size())
        throw std::out_of_range(msg);
}

template <typename T>
Blob<T>::Blob() : data(std::make_shared<std::vector<T>>()) {}

template <typename T>
Blob<T>::Blob(std::initializer_list<T> il) try : data(std::make_shared<std::vector<T>>(il)) {} catch(const std::bad_alloc &e) {cout << e.what() << "\n";}

template <typename T>
template <typename It>
Blob<T>::Blob(It b, It e) : data(std::make_shared<std::vector<T>>(b, e))
{
}

template <typename T>
void Blob<T>::pop_back()
{
    check(0, "pop_back on empty blob.");
    data->pop_back();
}

template <typename T>
T &Blob<T>::back()
{
    check(0, "back on empty blob.");
    return data->back();
}

template <typename T>
T &Blob<T>::operator[](size_type i)
{
    check(i, "subscript out of Blob range.");
    return (*data)[i];
}

template <typename T>
const T &Blob<T>::operator[](size_type i) const
{
    check(i, "subscript out of Blob range.");
    return (*data)[i];
}

template <typename T>
bool operator==(const Blob<T> &lhs, const Blob<T> &rhs)
{
    if (rhs.size() != lhs.size())
        return false;
    for (size_t i = 0; i < lhs.size(); ++i)
    {
        if (lhs[i] != rhs[i])
            return false;
    }
    return true;
}

// check member
template <typename T>
std::shared_ptr<std::vector<T>>
BlobPtr<T>::check(std::size_t i, const std::string &msg) const
{
    auto ret = wptr.lock(); // is the vector still around?
    if (!ret)
        throw std::runtime_error("unbound BlobPtr");
    if (i >= ret->size())
        throw std::out_of_range(msg);
    return ret; // otherwise, return a shared_ptr to the vector
}

template <typename T>
BlobPtr<T> &BlobPtr<T>::operator++()
{
    check(curr, "increment past end of BlobPtr");
    ++curr;
    return *this;
}

template <typename T>
BlobPtr<T> &BlobPtr<T>::operator--()
{
    --curr;
    check(-1, "increment past end of BlobPtr");
    return *this;
}

template <typename T>
BlobPtr<T> BlobPtr<T>::operator--(int)
{
    auto ret = *this;
    --(*this);
    return ret;
}

template <typename T>
BlobPtr<T> BlobPtr<T>::operator++(int)
{
    auto ret = *this;
    ++(*this);
    return ret;
}

template <typename T>
bool operator==(const BlobPtr<T> &lhs, const BlobPtr<T> &rhs)
{
    auto lhs_ret = lhs.check(0, "empty blob"), rhs_ret = rhs.check(0, "empty blob");

    return ((*lhs_ret == *rhs_ret) && (lhs.curr == rhs.curr)) ? true : false;
}

template <typename T>
BlobUseSharedPtr<T>::BlobUseSharedPtr() : data(new std::vector<T>()) {}

template <typename T>
void BlobUseSharedPtr<T>::check(size_type i, const std::string &msg) const
{
    if (i >= data->size())
        throw std::out_of_range(msg);
}

template <typename T>
void BlobUseSharedPtr<T>::pop_back()
{
    check(0, "pop_back on empty blob.");
    data->pop_back();
}

template <typename T>
T &BlobUseSharedPtr<T>::back()
{
    check(0, "back on empty blob.");
    return data->back();
}

template <typename T>
T &BlobUseSharedPtr<T>::operator[](size_type i)
{
    check(i, "subscript out of Blob range.");
    return (*data)[i];
}

template <typename T>
const T &BlobUseSharedPtr<T>::operator[](size_type i) const
{
    check(i, "subscript out of Blob range.");
    return (*data)[i];
}

template <typename T>
bool operator==(const BlobUseSharedPtr<T> &lhs, const BlobUseSharedPtr<T> &rhs)
{
    if (rhs.size() != lhs.size())
        return false;
    for (size_t i = 0; i < lhs.size(); ++i)
    {
        if (lhs[i] != rhs[i])
            return false;
    }
    return true;
}

template <pos H, pos W>
void ScreenT<H, W>::show(std::ostream &os) const
{
    for (int i = 0; i != H; ++i)
    {
        for (int j = 0; j != W; ++j)
        {
            os << contents[i * W + j] << ' ';
        }
        os << endl;
    }
}

template <pos H, pos W>
ScreenT<H, W> &ScreenT<H, W>::move(pos row, pos col)
{
    pos temp = cursor + row * W + col;

    if (temp < H * W)
        cursor = temp;
    else
    {
        cout << "your input row " << row << " and col " << col << " will move cursor" << cursor << " out of range" << endl;
    }

    return *this;
}

template <pos H, pos W>
ScreenT<H, W> &ScreenT<H, W>::set(pos row, pos col, char var)
{
    contents[row * W + col] = var;
    return *this;
}

template <pos H, pos W>
ScreenT<H, W> &ScreenT<H, W>::set(char var)
{
    contents[cursor] = var;
    return *this;
}

template <pos H, pos W>
ostream &operator<<(ostream &os, const ScreenT<H, W> &ST)
{
    ST.show(os);
    return os;
}

template <pos H, pos W>
istream &operator>>(istream &is, ScreenT<H, W> &ST)
{
    char temp;
    for (int i = 0; i != H; ++i)
    {
        for (int j = 0; j != W; ++j)
        {
            if (is >> temp)
            {
                ST.set(i, j, temp);
            }
        }
    }

    return is;
}

// WindowMgrT::WindowMgrT(pos h, pos w, char cts, size_t a)
// {
//     for (size_t s = 0; s != a; ++s)
//     {
//         ScreenT screen(h, w, cts);
//         screens.push_back(screen);
//     }
// }

// void WindowMgrT::clear(size_t a)
// {
//     if (a < screens.size())
//     {
//         ScreenT emptyScreen(0, 0, 0, ' ');
//         screens[a] = emptyScreen;
//     }
// }

// void WindowMgrT::show(std::ostream &os) const
// {
//     for (const auto &screen : screens)
//         screen.show(cout);
// }

// definition for static data member
template <typename T>
std::allocator<T> VecT<T>::alloc;

template <typename T>
VecT<T>::VecT(const VecT<T> &temp)
{
    auto ps_pair = alloc_n_copy(temp.begin(), temp.end());
    elements = ps_pair.first;
    first_free = ps_pair.second;
    cap = ps_pair.second;
}

template <typename T>
VecT<T> &VecT<T>::operator=(const VecT<T> &temp)
{
    auto ps_pair = alloc_n_copy(temp.begin(), temp.end());
    free();
    elements = ps_pair.first;
    first_free = ps_pair.second;
    cap = ps_pair.second;

    return *this;
}

template <typename T>
VecT<T> &VecT<T>::operator=(std::initializer_list<T> temp)
{
    auto newdata = alloc_n_copy(temp.begin(), temp.end());
    free();
    elements = newdata.first;
    first_free = cap = newdata.second;
}

template <typename T>
T &VecT<T>::operator[](size_t i)
{
    if (i >= size())
        throw std::runtime_error("Out of range\n");

    return *(elements + i);
}

template <typename T>
const T &VecT<T>::operator[](size_t i) const
{
    if (i >= size())
        throw std::runtime_error("Out of range\n");

    return *(elements + i);
}

template <typename T>
T VecT<T>::at(size_t i) const
{
    if (i >= size())
        throw std::runtime_error("Out of range\n");

    return *(elements + i);
}

template <typename T>
VecT<T>::VecT(VecT<T> &&s) noexcept
    : elements(s.elements), first_free(s.first_free), cap(s.cap)
{
    s.elements = s.first_free = s.cap = nullptr;
}

template <typename T>
VecT<T> &VecT<T>::operator=(VecT<T> &&s) noexcept
{
    if (this != &s)
    {
        free();
        elements = s.elements;
        first_free = s.first_free;
        cap = s.cap;
        s.elements = s.first_free = s.cap = nullptr;
    }
    return *this;
}

template <typename T>
VecT<T>::~VecT() noexcept
{
    free();
}

template <typename T>
VecT<T>::VecT(std::initializer_list<T> temp)
{
    auto newdata = alloc_n_copy(temp.begin(), temp.end());
    elements = newdata.first;
    first_free = cap = newdata.second;
}

template <typename T>
void VecT<T>::push_back(const T &s)
{
    cout << "(" << __FUNCTION__ << ")"
         << " push_back(const T &s)\n";
    chk_n_alloc();
    alloc.construct(first_free++, s);
}

template <typename T>
void VecT<T>::push_back(T &&s)
{
    cout << "(" << __FUNCTION__ << ")"
         << " push_back(T &&s)\n";
    chk_n_alloc();
    alloc.construct(first_free++, std::move(s));
}

template <typename T>
std::pair<T *, T *> VecT<T>::alloc_n_copy(const T *b, const T *e)
{
    // Let's try
    auto const p = alloc.allocate(e - b);
    return {p, std::uninitialized_copy(b, e, p)};
}

template <typename T>
void VecT<T>::free()
{
    if (elements)
    {
        // Way 1: original for loop
        // for (auto p = first_free; p != elements;)
        // {
        //     alloc.destroy(--p);
        // }

        // Way 2: iterator and lambda
        auto reverse_begin = boost::make_reverse_iterator(end());
        auto reverse_end = boost::make_reverse_iterator(begin());
        std::for_each(reverse_begin, reverse_end, [&](T &p)
                      { alloc.destroy(&p); });
        alloc.deallocate(elements, cap - elements);
    }
}

template <typename T>
void VecT<T>::reallocate()
{
    auto newcapacity = size() ? 2 * size() : 1;

    auto const p = alloc.allocate(newcapacity);
    auto dest = p;
    auto elem = elements;
    for (size_t i = 0; i != size(); ++i)
    {
        alloc.construct(dest++, std::move(*(elem++)));
    }
    free();
    elements = p;
    first_free = dest;
    cap = elements + newcapacity;
}

template <typename T>
bool operator==(const VecT<T> &lhs, const VecT<T> &rhs)
{
    return (lhs.size() == rhs.size()) && (std::equal(lhs.begin(), lhs.end(), rhs.begin()));
}

template <typename T>
bool operator!=(const VecT<T> &lhs, const VecT<T> &rhs)
{
    return !(lhs == rhs);
}

//  Above part is definition for class member funcs.

int exercise16_1() { return 0; }

int exercise16_2()
{
    unsigned int a = 100;
    long b = 1001;

    cout << "compare(a,b) : " << compare(a, b) << endl;

    unsigned int c = 1001;
    long d = 10;

    cout << "compare(c,d) : " << compare(c, d) << endl;

    return 0;
}

int exercise16_3()
{
    //     In instantiation of ‘int compare(const T1&, const T2&) [with T1 = Sales_data; T2 = Sales_data]’:
    //     required from here
    //     [build] /home/tian/projects/hellocpp/src/CppPrimer5th/chapter16/chapter16.h:53:16: error: no match for ‘operator<’ (operand types are ‘const Sales_data’ and ‘const Sales_data’)
    //     [build]      return (v1 < v2) ? 0 : 1;
    Sales_data a("abc"), b("def");

    // cout << "compare(a,b) : " << compare(a, b) << endl;

    return 0;
}

int exercise16_4()
{
    vector<int> a{1, 2, 3, 4, 5, 6};
    std::list<int> b{1, 2, 3, 4, 5, 6};

    auto c = find_test(a.begin(), a.end(), 5);

    if (c != a.end())
        cout << *c << endl;
    else
        cout << "Value not found" << endl;

    auto d = find_test(b.begin(), b.end(), 4);

    if (d != b.end())
        cout << *d << endl;
    else
        cout << "Value not found" << endl;

    auto e = find_test(a.cbegin(), a.cend(), 5);

    if (e != a.cend())
        cout << *e << endl;
    else
        cout << "Value not found" << endl;

    auto f = find_test(b.cbegin(), b.cend(), 4);

    if (f != b.end())
        cout << *f << endl;
    else
        cout << "Value not found" << endl;

    return 0;
}

int exercise16_5()
{
    char a[10] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b'};
    int b[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};

    print(a);

    print(b);

    return 0;
}

int exercise16_6()
{

    char a[10] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b'};
    int b[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
    cout << "exercise16_6:\n";
    for (auto temp = begin(a); temp != end(a); temp++)
    {
        cout << *temp << ",";
    }
    cout << endl;

    for (auto temp = begin(b); temp != end(b); temp++)
    {
        cout << *temp << ",";
    }
    cout << endl;

    return 0;
}

int exercise16_7()
{
    char a[10] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b'};
    int b[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};

    cout << sizeOfArray(a) << " ,,, " << sizeOfArray(b) << "\n";

    return 0;
}

int exercise16_8() { return 0; }

int exercise16_9() { return 0; }

int exercise16_10() { return 0; }

int exercise16_11() { return 0; }

int exercise16_12()
{
    Blob<string> str_blob{"abc", "abcd", "abcde", "abcdef"};
    Blob<string> sec_str_blob{"abc", "abcd", "abcde", "abcdef"};

    // cout << str_blob[0];

    if (str_blob == sec_str_blob)
    {
        cout << str_blob[3];
    }

    return 0;
}

int exercise16_13()
{
    Blob<string> str_blob{"abc", "abcd", "abcde", "abcdef"};
    Blob<string> sec_str_blob{"abc", "abcd", "abcde", "abcdef"};

    BlobPtr<string> str_blob_ptr(str_blob, 2), sec_str_blob_ptr(sec_str_blob, 3);

    if (str_blob_ptr == sec_str_blob_ptr)
        cout << *str_blob_ptr;
    else
        cout << *sec_str_blob_ptr;

    return 0;
}

int exercise16_14()
{
    ScreenT<4, 3> temp(10, '*');

    temp.set(3, 2, '&');
    temp.show(cout);
    cout << "--------------\n";
    cout << temp;

    return 0;
}

int exercise16_15()
{
    ScreenT<4, 3> temp(10, '*');
    temp.show(cout);
    cout << "--------------\n";
    cout << temp;

    cin >> temp;
    cout << "--------------\n";
    cout << temp;

    return 0;
}

int exercise16_16()
{
    string t1("temp str"), t2("temp str2"), t3("temp str3");
    VecT<std::string> a{t1, t2, t3}, b{t1, t2, t3};

    a.push_back(t1);
    b.push_back(t1);

    rangeForContainer(a);
    rangeForContainer(b);

    if (a == b)
    {
        cout << "Equal." << endl;
    }
    else
    {
        cout << "Not equal." << endl;
    }

    a.push_back(t2);
    b.push_back(t3);

    rangeForContainer(a);
    rangeForContainer(b);

    if (a == b)
    {
        cout << "Equal." << endl;
    }
    else
    {
        cout << "Not equal." << endl;
    }
    return 0;
}

int exercise16_17()
{
    return 0;
}

int exercise16_18()
{
    return 0;
}

int exercise16_19()
{
    vector<int> temp{5, 1, 23, 555, 6};

    rangeForContainerUseIndex(temp);
    return 0;
}

int exercise16_20()
{
    // see rangeForContainer
    return 0;
}

int exercise16_21()
{
    return 0;
}

int exercise16_22()
{
    return 0;
}

int exercise16_23()
{
    return 0;
}

int exercise16_24()
{
    vector<string> orig{"abc", "abcd", "abcde", "abcdef"};
    Blob<string> str_blob(orig.begin(), orig.end());

    for (auto temp = 0; temp != str_blob.size(); temp++)
    {
        cout << str_blob[temp] << ",";
    }
    cout << endl;

    return 0;
}

int exercise16_25()
{
    return 0;
}

int exercise16_26()
{
    return 0;
}

void f1(Stack<char>)
{
    cout << "1 \n";
}

int exercise16_27()
{
    Stack<char> *sc;                  // (d) Yes, an instantiation of Stack occurs for the type char.
    f1(*sc);                          // (e) Yes, copy *sc to f1's parameter
    int iObj = sizeof(Stack<string>); // (f) Yes, an instantiation of Stack occurs for the type std::string.

    return 0;
}

int exercise16_28()
{
    SharedPtr<int> a1;
    cout << "a1.useCount() = " << a1.useCount() << endl;
    {
        cout << "init a2 point to 999 \n";
        SharedPtr<int> a2(new int(999));
        cout << "a2.useCount() = " << a2.useCount() << endl;
        cout << "init a3 = a2 \n";
        SharedPtr<int> a3(a2);
        cout << "a3.useCount() = " << a3.useCount() << endl;

        cout << "init a1 = a3 \n";
        a1 = a3;
        cout << "a1.useCount() = " << a1.useCount() << endl;

        cout << "*a1 = " << *a1 << ", *a2 = " << *a2 << ", *a3 = " << *a3 << endl;

        cout << "reseting a3 point to 888 \n";
        a3.reset(new int(888));

        cout << "++(*a1.get()) \n";
        ++(*a1.get());

        cout << "*a1 = " << *a1 << ", *a2 = " << *a2 << ", *a3 = " << *a3 << endl;
        cout << "a1.useCount() = " << a1.useCount() << endl;
        cout << "a2.useCount() = " << a2.useCount() << endl;
        cout << "a3.useCount() = " << a3.useCount() << endl;
    }

    cout << "*a1 = " << *a1 << endl;
    cout << "a1.useCount() = " << a1.useCount() << endl;

    cout << "Testing UniquePtr \n";

    UniquePtr<int> u1(new int(1000));
    cout << "*u1 = " << *u1 << endl;

    cout << "reseting u1 point to 2000 \n";
    u1.reset(new int(2000));
    cout << "*u1 = " << *u1 << endl;

    UniquePtr<int> u3(new int(3000));

    int *t1 = u3.release();

    ++(*t1);
    cout << "t1 point to " << *t1 << endl;

    return 0;
}

int exercise16_29()
{
    BlobUseSharedPtr<string> str_blob;
    str_blob.push_back("abc");
    str_blob.push_back("abcd");
    str_blob.push_back("abcde");
    str_blob.push_back("abcdef");

    BlobUseSharedPtr<string> sec_str_blob;
    sec_str_blob.push_back("abc");
    sec_str_blob.push_back("abcd");
    sec_str_blob.push_back("abcde");
    sec_str_blob.push_back("abcdef");

    // cout << str_blob[0];

    if (str_blob == sec_str_blob)
    {
        cout << str_blob[3];
    }

    return 0;
}

int exercise16_30()
{
    return 0;
}

int exercise16_31()
{
    return 0;
}

int exercise16_32()
{
    return 0;
}

int exercise16_33()
{
    return 0;
}

int exercise16_34()
{
    // deduced conflicting types for parameter ‘const T’ (‘char [3]’ and ‘char [6]’)
    // cout << compareWrong("hi", "world");
    cout << compareWrong("bye", "dad");

    return 0;
}

int exercise16_35()
{
    double d(0.0);
    float f(0.3f);
    char c('A');
    // cout << calc(c, 'c') << endl;
    // cout << calc(d, f) << endl;
    cout << fcn(c, 'c') << endl;
    //  deduced conflicting types for parameter ‘T’ (‘double’ and ‘float’)
    // cout << fcn(d, f) << endl;
    return 0;
}

int exercise16_36()
{
    int i = 0, j = 42, *p1 = &i, *p2 = &j;
    const int *cp1 = &i, *cp2 = &j;
    f1(p1, p2);
    f2(p1, p2);
    f1(cp1, cp2);
    f2(cp1, cp2);
    // deduced conflicting types for parameter ‘T’ (‘int*’ and ‘const int*’)
    // f1(p1, cp1);
    f2(p1, cp1);

    return 0;
}

int exercise16_37()
{
    int i = 1000;
    double j = 999.9;

    cout << std::max<double>(static_cast<double>(i), j);

    return 0;
}

int exercise16_38()
{
    // IN shared_ptr.h：
    //   template<typename _Tp, typename... _Args>
    // inline shared_ptr<_Tp>
    // make_shared(_Args&&... __args)
    return 0;
}

int exercise16_39()
{
    string a("abc"), b("abvc");
    cout << compareWrong<std::string>(a, b);
    return 0;
}

int exercise16_40()
{

    int *a(new int(40));
    cout << fcn3(a, a);

    //     [build] /home/tian/projects/hellocpp/src/CppPrimer5th/chapter16/chapter16.h:804:6: note:   template argument deduction/substitution failed:
    // [build] /home/tian/projects/hellocpp/src/CppPrimer5th/chapter16/chapter16.h: In substitution of ‘template<class It> decltype (((* beg) + 0)) fcn3(It, It) [with It = std::basic_string<char>*]’:
    // [build] /home/tian/projects/hellocpp/src/CppPrimer5th/chapter16/chapter16.cpp:880:22:   required from here
    // [build] /home/tian/projects/hellocpp/src/CppPrimer5th/chapter16/chapter16.h:804:44: error: no match for ‘operator+’ (operand types are ‘std::basic_string<char>’ and ‘int’)
    // [build]  auto fcn3(It beg, It end) -> decltype(*beg + 0)

    // string * p(new string("abc"));
    // cout << fcn3(p, p);

    return 0;
}

int exercise16_41()
{
    long long llMax = std::numeric_limits<long long>::max();

    auto a(llMax), b(llMax);

    cout << a << endl;
    cout << sumTest(a, b);

    return 0;
}

int exercise16_42()
{
    return 0;
}

int exercise16_43()
{
    return 0;
}

int exercise16_44()
{
    return 0;
}

int exercise16_45()
{
    // template <typename T> void g(T&& val) { vector<T> v; }

    // g(42);       T: int; val: int &&; v: vector<int>
    // int i(10); g(i); T: int & ;  val: "int & &&" collapse to "int&"; v: vector<int &>
    // std::vector<int &> a;  // got lots of compiler error, we cannot init a vector with type int &
    return 0;
}

int exercise16_46()
{
    return 0;
}

int exercise16_47()
{
    auto f1 = [](int b, int a)
    { cout << (a + b) << endl; };

    auto f2 = [](int &b, int a)
    { cout << a + (b + 1) << endl; };

    auto f3 = [](int &&b, int a)
    { cout << a + (b + 1) << endl; };

    int a(10), b(20);
    flip(f1, a, b);  // T1 is int &, T2 is int &
    flip(f2, a, b);  // T1 is int &, T2 is int &
    flip(f3, a, 30); // T1 is int &, T2 is int.

    return 0;
}

int exercise16_48()
{
    string a1("test");
    debug_rep(a1);

    char a2[6] = "test2";
    debug_rep(a2);

    return 0;
}

int exercise16_49()
{
    int i = 42, *p = &i;
    const int ci = 0, *p2 = &ci;
    g(42);
    g(p);
    g(ci);
    g(p2);
    f(42);
    f(p);
    f(ci);
    f(p2);

    return 0;
}

int exercise16_50()
{
    return 0;
}

int exercise16_51()
{
    int i = 0;
    double d = 3.14, d2(3.3), d3(3.5);
    string s = "how now brown cow";
    foo(i, s, 42, d); // three parameters in the pack
    foo(s, 42, "hi"); // two parameters in the pack
    foo(d, s);        // one parameter in the pack
    foo("hi");        // empty pack

    foo("hi", d, d2, d3); // 使用参数包的时候，即使参数的类型都一样，类型参数依然会计数。

    return 0;
}

int exercise16_52()
{
    return 0;
}

int exercise16_53()
{
    int i = 0;
    double d = 3.14, d2(3.3), d3(3.5);
    string s = "how now brown cow";

    print(cout, i, d, d2, d3, s);

    return 0;
}

int exercise16_54()
{
    // compiler will output error if obejct doesn't have <<.
    // Blob<string> a;

    // a.push_back("abc");
    // a.push_back("bcd");

    // print(cout, a);

    return 0;
}

int exercise16_55()
{
    return 0;
}

int exercise16_56()
{
    int i = 0;
    double d = 3.14, d2(3.3), d3(3.5);
    string s = "how now brown cow";

    errorMsg(cout, i, d, d2, d3, s);

    return 0;
}

int exercise16_57()
{
    return 0;
}

int exercise16_58()
{
    string t1("temp str"), t2("temp str2"), t3("temp str3");
    StrVec a{t1, t2, t3};
    cout << a.size() << "\n"
         << a.capacity() << "\n";

    a.emplace_back(10, 'c');
    cout << a.size() << "\n"
         << a.capacity() << "\n"
         << a.at(3) << "\n";

    Blob<string> str_blob{"abc", "abcd", "abcde", "abcdef"};
    str_blob.emplace_back(10, 'c');

    cout << str_blob.size() << "\n"
         << str_blob[4] << "\n";

    return 0;
}

int exercise16_59()
{
    return 0;
}

int exercise16_60()
{
    return 0;
}

int exercise16_61()
{
    string a("abc");
    std::shared_ptr<string> b = make_shared_test<string>(10, 'c');

    cout << *b << endl;

    return 0;
}

int exercise16_62()
{

    Sales_data a("abc", 10, 1.11), b("abcd", 110, 2.22), c("abc", 10, 1.11), d("abc", 10, 1.11);
    std::unordered_multiset<Sales_data> SDset = {a, b, c, d};

    auto test_a = SDset.count(a);
    auto iter = SDset.find(a);

    cout << "Number " << test_a << " of a found.\n";
    cout << *iter << "\n";

    return 0;
}

int exercise16_63()
{
    return 0;
}

int exercise16_64()
{
    return 0;
}

int exercise16_65()
{
    return 0;
}

int exercise16_66()
{
    return 0;
}

int exercise16_67()
{
    return 0;
}